#include "../build/stateStore/soapH.h"
#include "../build/stateStore/AWSMStore.nsmap"
#include "libservice.h"

char *serviceName = "AWSMStore";
char * uuid;

char * get_service(struct soap *soap, char * addressWsdl) {
	// return physical address of a service wsdl
	xmlDoc *doc = NULL;
	xmlNode *root = NULL;
	xmlNode *service = NULL;

	char * service_file;
	config_get_string(&service_file, "SERVICES_FILE");

	doc = xmlReadFile(service_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		fprintf(stderr, "stateStore.c: deployment file %s doesn't exist",
				service_file);
		return NULL;
	}

	root = xmlDocGetRootElement(doc);
	service = root ->children;

	while (service) {
		if (strstr(xmlGetProp(service, "addressWsdl"), addressWsdl)) {
			return xmlGetProp(service, "path");
		}
		service = service->next;
	}

	xmlFreeDoc(doc);

	return NULL;

}

int http_get(struct soap *soap) {
	// implements HTTP GET method
	char *s = strchr(soap->path, '?');
	if (!s || strcmp(s, "?wsdl"))
		return SOAP_GET_METHOD;
	FILE * f = fopen(get_service(soap, soap->path), "rb");
	if (!f)
		return 404;
	soap->http_content = "text/xml";
	soap_response(soap, SOAP_FILE);
	for (;;) {
		int r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), f);
		if (!r)
			break;
		if (soap_send_raw(soap, soap->tmpbuf, r))
			break;
	}
	fclose(f);
	soap_end_send(soap);
	return SOAP_OK;
}

void *process_request(void *soap) {
	pthread_detach(pthread_self());
	soap_serve((struct soap*) soap);
	soap_destroy((struct soap*) soap);
	// dealloc C++ data
	soap_end((struct soap*) soap);
	// dealloc data and clean up
	soap_done((struct soap*) soap);
	// detach soap struct
	free(soap);
	return NULL;
}

int main(int argc, char **argv) {

	struct soap soap;
	soap_init(&soap);
	soap.fget = http_get;

	soap.send_timeout = 5;
	soap.recv_timeout = 5;
	soap.accept_timeout = 3600;
	// server stops after 1 hour of inactivity
	soap.max_keep_alive = 100;
	// max keep-alive sequence

	void *process_request(void*);
	struct soap *tsoap;
	pthread_t tid;
	SOAP_SOCKET m, s;

	int state_store_port;
	config_get_int(&state_store_port, "STATE_STORE_PORT");
	char * state_store_ip;
	config_get_string(&state_store_ip, "STATE_STORE_IP");

	m = soap_bind(&soap, NULL, state_store_port, 100);

	if (!soap_valid_socket(m)) {
		fprintf(stderr, "stateStore.c: %s failed to start, port %d is busy\n",
				serviceName, state_store_port);
		exit(1);
	}

	service_set_uuid(&uuid);
	char *serviceWsdl = malloc(256);
	sprintf(serviceWsdl, "http://%s:%d/AWSMStore?wsdl", state_store_ip,
			state_store_port);
	char * awsm_home;
	config_get_string(&awsm_home, "AWSM_HOME");
	service_deploy(serviceName, serviceWsdl, strcat(awsm_home,
			"/build/stateStore/AWSMStore.wsdl"), uuid);
	fprintf(stderr, "stateStore.c: %s with id %s started\n", serviceName, uuid);

	for (;;) {
		s = soap_accept(&soap);
		if (!soap_valid_socket(s)) {
			if (soap.errnum) {
				soap_print_fault(&soap, stderr);
				exit(1);
			}
			fprintf(stderr, "stateStore.c: server timed out\n");
			service_undeploy(serviceName, uuid);
			break;
		}
		fprintf(stderr,
				"[soap] stateStore.c: request from IP %d.%d.%d.%d processed\n",
				(soap.ip >> 24) & 0xFF, (soap.ip >> 16) & 0xFF, (soap.ip >> 8)
						& 0xFF, soap.ip & 0xFF);
		tsoap = soap_copy(&soap);
		// make a safe copy
		if (!tsoap)
			break;
		pthread_create(&tid, NULL, (void*(*)(void*)) process_request,
				(void*) tsoap);
	}

	soap_done(&soap);
	// detach soap struct
	return 0;

}

int ns__store(struct soap *soap, char * id, char * statesXml, char ** message) {
	xmlDoc *doc = NULL;
	xmlNode *root = NULL;
	xmlNode *states = NULL;
	xmlNode *state = NULL;

	xmlDoc *stateNewDoc = NULL;
	xmlNode *stateNewRoot = NULL;
	xmlNode *statesNew = NULL;

	xmlDoc *stateToAddDoc = NULL;
	xmlNode *stateToAdd = NULL;

	stateToAddDoc = xmlParseDoc(statesXml);
	stateToAdd = xmlDocGetRootElement(stateToAddDoc);

	char * state_store_file;
	config_get_string(&state_store_file, "STATE_STORE_FILE");

	pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_lock(&cs_mutex);
	//enter critical section

	doc = xmlReadFile(state_store_file, NULL, XML_PARSE_NOWARNING);

	stateNewDoc = xmlNewDoc("1.0");
	stateNewRoot = xmlNewNode(NULL, "statestore");
	xmlNs * namespace = xmlNewNs(stateNewRoot,
			"http://www-vs.informatik.uni-ulm.de/proj/samproc", "ns");
	stateNewRoot->ns = namespace;

	xmlDocSetRootElement(stateNewDoc, stateNewRoot);

	int update = 0;

	if (doc) {
		root = xmlDocGetRootElement(doc);
		states = root->children;

		while (states) {

			if (xmlStrEqual(id, xmlGetProp(states, "service-id"))) {
				//add states with same id
				xmlNewProp(stateToAdd, "service-id", id);
				xmlAddChild(stateNewRoot, xmlCopyNode(stateToAdd, 1));
				update = 1;
			} else {
				xmlAddChild(stateNewRoot, xmlCopyNode(states, 1));
				//add other states
			}
			states = states->next;
		}
	}

	if (!update) {
		xmlNewProp(stateToAdd, "service-id", id);
		xmlAddChild(stateNewRoot, xmlCopyNode(stateToAdd, 1));
	}

	xmlSaveFile(state_store_file, stateNewDoc);
	*message = soap_malloc(soap, 1024);
	sprintf(*message, "stateStore.c: states %s stored", id);

	pthread_mutex_unlock(&cs_mutex);
	pthread_mutex_destroy(&cs_mutex);

	xmlFreeDoc(doc);
	xmlFreeDoc(stateToAddDoc);
	xmlFreeDoc(stateNewDoc);

	return SOAP_OK;

}

int ns__getState(struct soap *soap, char* id, char ** statesXml) {
	xmlDoc * doc = NULL;
	xmlNode * root = NULL;
	xmlNode * states = NULL;
	xmlNode * state = NULL;
	*statesXml = NULL;

	char * state_store_file;
	config_get_string(&state_store_file, "STATE_STORE_FILE");

	doc = xmlReadFile(state_store_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		char *s = soap_malloc(soap, 1024);
		sprintf(s, "statestore file %s doesn't exist", state_store_file);
		return soap_sender_fault(soap, s, NULL);
	}

	root = xmlDocGetRootElement(doc);
	states = root->children;

	*statesXml = soap_malloc(soap, 1024);
	sprintf(*statesXml,
			"<ns:statestore xmlns:ns=\"http://www-vs.informatik.uni-ulm.de/proj/samproc\">");
	while (states) {
		if (xmlStrEqual(id, xmlGetProp(states, "service-id"))) {
			char *tmp = soap_malloc(soap, 1024);
			sprintf(tmp, "<states service-id='%s'>", id);
			*statesXml = strcat(*statesXml, tmp);

			state = states->children;
			while (state) {

				sprintf(tmp, "<state type='%s' name='%s' length='%s'>",
						xmlGetProp(state, "type"), xmlGetProp(state, "name"),
						xmlGetProp(state, "length"));
				*statesXml = strcat(*statesXml, tmp);
				sprintf(tmp, "%s</state>", xmlNodeListGetString(doc,
						state->children, 1));
				*statesXml = strcat(*statesXml, tmp);

				state = state->next;
			}

			*statesXml = strcat(*statesXml, "</states>");
			break;
		}

		states = states->next;
	}
	*statesXml = strcat(*statesXml, "</ns:statestore>");
	xmlFreeDoc(doc);
	return SOAP_OK;

}

int ns__getStateVariables(struct soap *soap, char *id,
		struct ArrayOfString * variables, char **statesXml) {
	xmlDoc * doc = NULL;
	xmlNode * root = NULL;
	xmlNode * states = NULL;
	xmlNode * state = NULL;

	char * state_store_file;
	config_get_string(&state_store_file, "STATE_STORE_FILE");

	doc = xmlReadFile(state_store_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		char *s = soap_malloc(soap, 1024);
		sprintf(s, "statestore file %s doesn't exist", state_store_file);
		return soap_sender_fault(soap, s, NULL);
	}

	root = xmlDocGetRootElement(doc);
	states = root->children;

	*statesXml = soap_malloc(soap, 1024);
	sprintf(*statesXml,
			"<ns:statestore xmlns:ns=\"http://www-vs.informatik.uni-ulm.de/proj/samproc\">");
	while (states) {
		if (xmlStrEqual(id, xmlGetProp(states, "service-id"))) {
			char *tmp = soap_malloc(soap, 1024);
			sprintf(tmp, "<states service-id='%s'>", id);
			*statesXml = strcat(*statesXml, tmp);
			int i;
			for (i = 0; i < variables->__size; i++) {
				state = states->children;
				while (state) {
					if (xmlStrEqual(xmlGetProp(state, "name"),
							variables->__ptr[i])) {
						sprintf(tmp, "<state type='%s' name='%s' length='%s'>",
								xmlGetProp(state, "type"), xmlGetProp(state,
										"name"), xmlGetProp(state, "length"));
						*statesXml = strcat(*statesXml, tmp);
						sprintf(tmp, "%s</state>", xmlNodeListGetString(doc,
								state->children, 1));
						*statesXml = strcat(*statesXml, tmp);
					}
					state = state->next;
				}
			}

			*statesXml = strcat(*statesXml, "</states>");
			break;
		}

		states = states->next;
	}
	*statesXml = strcat(*statesXml, "</ns:statestore>");
	xmlFreeDoc(doc);
	return SOAP_OK;
}
